Access to reliable electricity remains a significant challenge across sub-Saharan Africa. Nigeria, Africa’s most populous and the continent’s second largest economy, continues to grapple with widespread energy poverty, impacting households, productive sectors, and essential public service delivery.
To support more targeted and evidence-based energy planning, this analysis leverages satellite-derived nighttime lights data as a spatial proxy for electricity access. Specifically, daily observations from the Visible Infrared Imaging Radiometer Suite (VIIRS) sensor, using the Gap-Filled DNB BRDF-Corrected Nighttime Lights product at 500-meter resolution, was used to analyze lighting patterns across Nigeria from 2022 to 2024. Daily VIIRS nightlights data from 2022 to 2024 (Gap_Filled_DNB_BRDF_Corrected_NTL band) were composited into annual mean rasters covering the period 2020-2024.
By analyzing the intensity and spatial distribution of nighlights across Nigeria, we estimate the electrification footprint and highlight persistently dark or under-served areas that may lack grid connectivity. While the methodology is optimised for in-country analysis, the results are not intended for cross-country comparisons as normalisation was performed using Nigeria specific distributions.
This analysis provides a foundation for more granular energy access assessments and helps identify high-priority locations for solar and decentralized energy interventions, particularly in rural and peri-urban areas with limited infrastructure.
To estimate spatial patterns of electricity access across Nigeria, satellite-derived nighttime light intensity data (VIIRS, 500m resolution, 2022–2024) were normalized, transformed and analysed using unsupervised clustering techniques to improve comparability across geographic areas. Several transformation techniques were tested, including logarithmic scaling, square root, Box-Cox, and histogram equalization, to optomise the distribution of light values. Ultimately, the normalized mean raster was selected for clustering, as it exhibited a near-symmetric, bell-shaped distribution ideal for unsupervised classification.
Two clustering algorithms were evaluated: K-Means and Gaussian Mixture Modeling (GMM). Both methods produced broadly consistent results, with over 80% spatial overlap in cluster assignments. However, GMM was selected for the final classification due to its probabilistic structure, which better captures gradual transitions and ambiguity, particularly in peri-urban or semi-electrified areas where light intensity changes are not abrupt.
Unlike K-Means, which imposes hard boundaries between clusters, GMM models overlapping distributions and is therefore more flexible in representing mixed or transitional access zones. Silhouette analysis supported a four-cluster solution, offering the best trade-off between statistical cohesion and interpretability.
The final classification assigned each 500m pixel to one of four electricity access tiers based on the distribution of normalized light intensity values:
No/Very Low Access: ≤ 0.612
Low Access: 0.612 – 0.730
Medium Access: 0.730 – 0.890
High Access: > 0.890
This GMM-based typology provides a spatially continuous, high-resolution map of electricity access across Nigeria. It enables a more targeted planning and identification of under-served populations, including hidden deprivation within partially electrified regions.
The four maps depict distinct typologies of energy access derived from the GMM classification. Each typology corresponds to a different access tier and reveals the spatial distribution of energy inequality across Nigeria:
Dark purple areas indicate regions with no or minimal electricity access. These zones are predominantly located across the northern belt and represent the most energy-deprived parts of the country.
Blue areas represent zones with limited but existing access, often forming transitional buffers between poorly electrified regions and areas with moderate access. These tend to cluster around rural-urban interfaces or semi-electrified zones.
Green areas correspond to moderate electricity access, typically forming a halo around major cities and dense settlements. They are most prominent in the middle belt and northern fringes of southern Nigeria.
Yellow areas denote high electricity access, concentrated around major urban hubs such as Lagos, Abuja, and Port Hartcourt, as well as in the oil-producing Niger Delta. These areas also appear around isolated city centers in the north, highlighting localized electrification.
Together, the maps provide a nuanced spatial typology of electricity access, enabling visual identification of priority regions for energy investment and solar deployment.
To pinpoint where energy deprivation intersects most critically with human settlement, the analysis overlays the GMM classified electricity access typologies with smoothed population density surfaces derived from the Global Human Settlement Layer (GHS-POP). GHS-POP provides gridded population estimates at ~1km resolution, enabling disaggregated spatial analysis across the four electricity access tiers.
To emphasize regional clusters over isolated outliers, a 51×51 pixel kernel smoothing filter was applied to the population rasters within each access category. This revealed not only where people live, but how densely they are concentrated. Pixels with fewer than 100 people/km² were rendered in gray to de-emphasize sparsely populated zones and instead highlight areas with concentrated human presence.
The resulting maps reveal a stark spatial mismatch in many parts of the country: large, densely populated areas, particularly across northern Nigeria and the Middle Belt, fall within zones classified as having very low or no electricity access. These hotspots represent high-priority intervention zones, where infrastructure deficits directly affect large populations.
Conversely, high-access zones align closely with major urban centers particularly in the southern regions of the country, confirming an urban–infrastructure nexus. Medium access areas form a transitional ring around secondary cities and expanding peri-urban zones, while low access areas show scattered yet significant population densities, often in emerging rural towns or underserved settlements.
Some areas within the “very low access” class appear sparsely populated. This is expected in locations such as national parks, wetlands, or terrain unsuitable for habitation, and may also reflect limitations of satellite-based population detection in dispersed rural areas.
By intersecting electricity access classifications with population density, the analysis shifts from infrastructure mapping to human-centered energy diagnostics, providing a sharper basis for prioritizing interventions based on real-world needs.
This section disaggregates electricity access and energy poverty at the state level, combining GMM-classified nightlight-based access typologies with high-resolution population estimates (GHS-POP 2025) to reveal the scale and spatial distribution of under-served populations across Nigeria. While these are population estimates derived from the GHS-POP model, they still offer valuable spatial insight into settlement patterns and the population distribution of energy poverty.
By overlaying each electricity access class, ranging from No/Very Low to High Access, with gridded population surfaces (~1 km resolution), the analysis generates population-adjusted estimates of energy deprivation. This allows not only quantification of affected populations, but also identification of where energy poverty is most concentrated within each state.
To visualize these dynamics, the card presents three linked views:
A national bar chart (top left) estimating population by access level, showing overall distribution;
A state-level stacked bar chart (bottom left) showing the percentage of each state’s population in each access tier;
An interactive map (right) depicting the share of population in energy poverty—defined as those residing in No/Very Low or Low Access zones.
A bubble chart (bottom right) plotting each state by percentage of population in living in No/Very Low and Low energy access areas (X axis) against estimated total energy poor population (Y axis) and the size of the bubble reflecting total population at the state level.
Together, these views highlight both relative and absolute disparities. They help identify priority states with both high energy poverty rates and large affected populations For instance, two states may have similar percentages of energy-poor populations, but vastly different population sizes—amplifying the need for population-adjusted targeting in energy access planning.
The analysis was extended to cater for a more operational and locally actionable scale, energy access estimates were disaggregated to Nigeria’s 774 LGAs. This LGA-level view allows for finer geographic targeting and supports more tailored interventions, especially in high-burden and under-served communities that may be overlooked in state-level averages.
To synthesize two dimensions of energy poverty intensity (% of population without access) and scale (absolute number affected), a composite Energy Access Deficit score was developed. This score combines normalized values of energy poverty percentage and estimated energy-poor population to flag LGAs where unmet energy needs are both widespread and numerically large.
The interactive map displays LGA-level Energy Access Deficit scores across Nigeria, while the accompanying searchable table provides access statistics and scores by LGA and state.
Note: Population estimates are derived from modeled data (GHS-POP 2025) and should be interpreted as indicative. While not based on recent census data, they offer valuable insight into spatial patterns of deprivation and remain useful for planning and prioritization.
[graphics to be rendered] This section overlays public hospital locations with GMM-classified electricity access typologies to highlight the availability of energy infrastructure at health facilities across Nigeria. By categorizing each hospital into four access levels—No/Very Low, Low, Medium, and High—the analysis identifies critical service points operating under severe energy constraints.
Public hospitals in No/Very Low Access zones often serve as primary health centers for underserved communities. Identifying and mapping these facilities provides a clear entry point for infrastructure investment planning—especially solarization or mini-grid solutions.
To support prioritization, the dashboard presents:
A state-level bar chart showing the percentage of hospitals in No/Low Access areas
An interactive map of hospital points, color-coded by access level
Summary counts of hospitals per access level
Note: Only public hospitals were included (federal, state, and local government facilities).
Critical Healthcare Infrastructure Findings:
Priority States for Healthcare Energy Interventions: The northern states of Borno, Katsina, Bauchi, and Jigawa emerge as top priorities, combining high numbers of energy-poor healthcare facilities with large populations lacking electricity access.
This analysis underscores the critical intersection between energy poverty and healthcare delivery, where reliable electricity is essential for medical equipment, vaccine cold chains, lighting for night deliveries, and overall healthcare quality.
To do
---
title: "UNDP Nigeria Knowledge Hub | Mapping Energy Access and Solar Potential"
output:
flexdashboard::flex_dashboard:
storyboard: true
social: menu
source: embed
css: styles.css
scroll: yes
---
```{r setup, include=FALSE, results = 'asis'}
pacman::p_load(flexdashboard,leaflet,leaflet.extras,sf,RColorBrewer,terra,raster, viridis,tmap, shiny,htmltools, plotly, tidyr, dplyr, DT, crosstalk)
terraOptions(progress = 0)
```
### **Mapping Energy Access and Solar Potential** <br> UNDP Nigeria Data Hub
```{r, cover-page, echo = FALSE, warnings = FALSE}
cover_card <- tags$div(
style = "position: relative; width: 100%; height: 100vh; margin: 0; padding: 0; overflow: hidden; border-radius: 0;",
# Temp image- ask Comms team for Nieria image
tags$img(
src = "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/CoverPage_Test.jpg",
style = "width: 100%; height: 100%; object-fit: cover; filter: brightness(0.6);"
),
# Top text
tags$div(
"Mapping Energy Access in Nigeria",
style = "position: absolute; top: 48%; left: 50%;
transform: translate(-50%, -50%);
font-size: 42px; font-weight: bold;
color: white; text-align: center;
font-family: 'Helvetica Neue', sans-serif;"
),
# Subtitle
tags$div(
style = "position: absolute; top: 58%; left: 50%; transform: translate(-50%, -50%);
font-size: 22px; font-weight: normal;
color: white; text-align: center;",
"UNDP Nigeria", tags$br(), "Data Hub"
))
cover_card
```
### **Nigeria at Night**<br>VIIRS Daily Composite Averages (2022–2024)
```{r nightlights-map, fig.width=12, fig.height=8, echo=FALSE, message=FALSE, fig.bg = "black", warning=FALSE}
tiff_path <- "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/mean_raster_norm.tif"
raster_data <- rast(tiff_path)
# Transform for visual enhancement
mean_raster_vis <- raster_data^3
r_proj <- project(mean_raster_vis, "EPSG:3857")
r_downsampled <- aggregate(r_proj, fact = 4, fun = mean)
nasa_palette <- colorRampPalette(c(
"#000000",
"#0C0C2C",
"#1F1F4D",
"#4A3005",
"#FFB100",
"#FFF5CC"
))
colors <- nasa_palette(100)
pal <- colorNumeric(palette = colors, domain = values(r_proj), na.color = "transparent")
#par(bg = "black", mar = c(0, 0, 0, 0))
#plot(mean_raster_vis,
# col = colors,
# axes = FALSE,
# box = FALSE,
# legend = FALSE,
#main = "",
#bg = "black")
leaflet() %>%
addProviderTiles(providers$CartoDB.DarkMatter) %>%
addRasterImage(r_downsampled, colors = pal, opacity = 0.9) %>%
addLegend(pal = pal, values = values(r_proj), title = "Nightlight <br> Intensity") %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 7) %>%
addControl(
html = HTML("<div style='font-size:10px; color:gray; padding:4px;'>*Note: Night light data shown for Nigeria only. </div>"),
position = "bottomleft"
)
```
***
Access to reliable electricity remains a significant challenge across sub-Saharan Africa. Nigeria, Africa's most populous and the continent’s second largest economy, continues to grapple with widespread energy poverty, impacting households, productive sectors, and essential public service delivery.
To support more targeted and evidence-based energy planning, this analysis leverages satellite-derived nighttime lights data as a spatial proxy for electricity access. Specifically, daily observations from the Visible Infrared Imaging Radiometer Suite (VIIRS) sensor, using the Gap-Filled DNB BRDF-Corrected Nighttime Lights product at 500-meter resolution, was used to analyze lighting patterns across Nigeria from 2022 to 2024. Daily VIIRS nightlights data from 2022 to 2024 (Gap_Filled_DNB_BRDF_Corrected_NTL band) were composited into annual mean rasters covering the period 2020-2024.
By analyzing the intensity and spatial distribution of nighlights across Nigeria, we estimate the electrification footprint and highlight persistently dark or under-served areas that may lack grid connectivity. While the methodology is optimised for in-country analysis, the results are not intended for cross-country comparisons as normalisation was performed using Nigeria specific distributions.
This analysis provides a foundation for more granular energy access assessments and helps identify high-priority locations for solar and decentralized energy interventions, particularly in rural and peri-urban areas with limited infrastructure.
### **Energy Access Clusters** <br> Gaussian Mixture Model (GMM) Classification
```{r gmm, warning = FALSE, message = FALSE, echo = FALSE}
# cluster_colors <- c( "#253494", # Dark blue - Low/No Access, "#2c7fb8", # Teal Blue - Moderate Access, "#fd8d3c", # Orange - High Access, "#ffffcc" # Light Yellow - Highly Accessed)
# Load the raster
tiff_path <- "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/access_level_raster.tif"
raster_data <- rast(tiff_path)
r_proj <- project(raster_data, "EPSG:4326")
# Downsample
r_downsampled <- aggregate(r_proj, fact = 2, fun = modal)
raster_r <- raster(r_downsampled)
# Colors
cluster_colors <- c("#440154FF", "#31688EFF", "#35B779FF", "#FDE725FF")
cluster_labels <- c("Very Low/No Access", "Low Access", "Medium Access", "High Access")
pal <- colorFactor(palette = cluster_colors, domain = 1:4, na.color = "transparent")
# Map
leaflet() %>%
addProviderTiles("CartoDB.DarkMatter") %>%
# Add masked raster
addRasterImage(raster_r, colors = pal, opacity = 0.8, project = FALSE) %>%
# Legend
addLegend(position = "topright",
colors = cluster_colors,
labels = cluster_labels,
title = "Electricity Access Clusters",
opacity = 1) %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 7)
```
***
To estimate spatial patterns of electricity access across Nigeria, satellite-derived nighttime light intensity data (VIIRS, 500m resolution, 2022–2024) were normalized, transformed and analysed using unsupervised clustering techniques to improve comparability across geographic areas. Several transformation techniques were tested, including logarithmic scaling, square root, Box-Cox, and histogram equalization, to optomise the distribution of light values. Ultimately, the normalized mean raster was selected for clustering, as it exhibited a near-symmetric, bell-shaped distribution ideal for unsupervised classification.
Two clustering algorithms were evaluated: K-Means and Gaussian Mixture Modeling (GMM). Both methods produced broadly consistent results, with over 80% spatial overlap in cluster assignments. However, GMM was selected for the final classification due to its probabilistic structure, which better captures gradual transitions and ambiguity, particularly in peri-urban or semi-electrified areas where light intensity changes are not abrupt.
Unlike K-Means, which imposes hard boundaries between clusters, GMM models overlapping distributions and is therefore more flexible in representing mixed or transitional access zones. Silhouette analysis supported a four-cluster solution, offering the best trade-off between statistical cohesion and interpretability.
The final classification assigned each 500m pixel to one of four electricity access tiers based on the distribution of normalized light intensity values:
- No/Very Low Access: ≤ 0.612
- Low Access: 0.612 – 0.730
- Medium Access: 0.730 – 0.890
- High Access: > 0.890
This GMM-based typology provides a spatially continuous, high-resolution map of electricity access across Nigeria. It enables a more targeted planning and identification of under-served populations, including hidden deprivation within partially electrified regions.
### **Energy Access Typologies** <br> Cluster-Specific Spatial Patterns
```{r access-panels, echo=FALSE}
library(leaflet)
library(raster)
library(htmltools)
# Color palette
colors <- c("#6A51A3", "#2B8CBE", "#31A354", "#FED976")
labels <- c("Very Low/No Access", "Low Access", "Medium Access", "High Access")
# Cluster level raster
level_rasters <- lapply(1:4, function(i) {
r <- raster_r
r[r != i] <- NA
r
})
# HTML containers
map_tags <- lapply(1:4, function(i) {
pal <- colorNumeric(palette = colors[i], domain = i, na.color = "transparent")
map <- leaflet() %>%
addProviderTiles(providers$CartoDB.DarkMatter) %>%
addRasterImage(level_rasters[[i]], colors = pal, opacity = 0.8, project = FALSE) %>%
addLegend("bottomright", colors = colors[i], labels = labels[i], title = labels[i], opacity = 1) %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 6)
# Use flexible layout
tags$div(
style = "flex: 1 1 48%; height: 45vh; margin: 1%;",
map
)
})
# Wrap maps
browsable(
tags$div(
style = "display: flex; flex-wrap: wrap; justify-content: space-between; min-height: 900px; width: 100%;",
map_tags
)
)
```
***
The four maps depict distinct typologies of energy access derived from the GMM classification. Each typology corresponds to a different access tier and reveals the spatial distribution of energy inequality across Nigeria:
- Dark purple areas indicate regions with **no or minimal** electricity access. These zones are predominantly located across the northern belt and represent the most energy-deprived parts of the country.
- Blue areas represent zones with **limited but existing access**, often forming transitional buffers between poorly electrified regions and areas with moderate access. These tend to cluster around rural-urban interfaces or semi-electrified zones.
- Green areas correspond to **moderate** electricity access, typically forming a halo around major cities and dense settlements. They are most prominent in the middle belt and northern fringes of southern Nigeria.
- Yellow areas denote **high** electricity access, concentrated around major urban hubs such as Lagos, Abuja, and Port Hartcourt, as well as in the oil-producing Niger Delta. These areas also appear around isolated city centers in the north, highlighting localized electrification.
Together, the maps provide a nuanced spatial typology of electricity access, enabling visual identification of priority regions for energy investment and solar deployment.
### **Population Density and Energy Access** Identifying Underserved High-Density Areas
```{r, population-density, echo=FALSE, message=FALSE, warning=FALSE, results='asis'}
# -----------------------------
# 1. Load Population Density Raster
# -----------------------------
pop1_density <- rast("/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/pop1_density.tif")
pop2_density <- rast("/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/pop2_density.tif")
pop3_density <- rast("/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/pop3_density.tif")
pop4_density <- rast("/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/pop4_density.tif")
# -----------------------------
# 2. Convert Rasters
# -----------------------------
rasters <- list(raster(pop1_density), raster(pop2_density),
raster(pop3_density), raster(pop4_density))
titles <- c("No/Very Low Access Areas", "Low Access Areas", "Medium Access Areas", "High Access Areas")
# -----------------------------
# 3. Define density breaks and color ramps
# -----------------------------
smooth_breaks <- c(0, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000) # Reintroduce 0–100
access_palettes <- list(
colorRampPalette(c("#F6EFF7", "#440154")), # purple
colorRampPalette(c("#E0ECF4", "#31688E")), # blue
colorRampPalette(c("#E5F5E0", "#35B779")), # green
colorRampPalette(c("#FFFACD", "#FDE725")) # yellow
)
# -----------------------------
# 4. Generate Maps
# -----------------------------
map_tags <- lapply(1:4, function(i) {
colors_raw <- access_palettes[[i]](length(smooth_breaks) - 1)
colors_raw[1] <- "#D9D9D9" # Set the 0–100 bin color
pal <- colorBin(
palette = colors_raw,
bins = smooth_breaks,
na.color = "transparent"
)
map <- leaflet() %>%
addProviderTiles(providers$CartoDB.DarkMatter) %>%
addRasterImage(rasters[[i]], colors = pal, opacity = 0.9, project = FALSE) %>%
addLegend(
position = "bottomright",
pal = pal,
values = values(rasters[[i]]),
title = paste(titles[i], "\n(Population Density)"),
opacity = 1
) %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 6)
tags$div(
style = "flex: 1 1 48%; height: 45vh; min-height: 400px; margin: 1%; box-sizing: border-box;",
map
)
})
# -----------------------------
# 5. Render all in flex layout
# -----------------------------
browsable(
tags$div(
style = "display: flex; flex-wrap: wrap; justify-content: space-between; align-items: stretch; width: 100%; min-height: 100vh;",
map_tags
)
)
```
***
To pinpoint where energy deprivation intersects most critically with human settlement, the analysis overlays the GMM classified electricity access typologies with smoothed population density surfaces derived from the Global Human Settlement Layer (GHS-POP). GHS-POP provides gridded population estimates at ~1km resolution, enabling disaggregated spatial analysis across the four electricity access tiers.
To emphasize regional clusters over isolated outliers, a 51×51 pixel kernel smoothing filter was applied to the population rasters within each access category. This revealed not only where people live, but how densely they are concentrated. Pixels with fewer than 100 people/km² were rendered in gray to de-emphasize sparsely populated zones and instead highlight areas with concentrated human presence.
The resulting maps reveal a stark spatial mismatch in many parts of the country: large, densely populated areas, particularly across northern Nigeria and the Middle Belt, fall within zones classified as having very low or no electricity access. These hotspots represent high-priority intervention zones, where infrastructure deficits directly affect large populations.
Conversely, high-access zones align closely with major urban centers particularly in the southern regions of the country, confirming an urban–infrastructure nexus. Medium access areas form a transitional ring around secondary cities and expanding peri-urban zones, while low access areas show scattered yet significant population densities, often in emerging rural towns or underserved settlements.
Some areas within the “very low access” class appear sparsely populated. This is expected in locations such as national parks, wetlands, or terrain unsuitable for habitation, and may also reflect limitations of satellite-based population detection in dispersed rural areas.
By intersecting electricity access classifications with population density, the analysis shifts from infrastructure mapping to human-centered energy diagnostics, providing a sharper basis for prioritizing interventions based on real-world needs.
### **State-Level Energy Poverty Estimates** <br> Disaggregating Electricity Access by Population and Geography
```{r energy-poverty-state, echo=FALSE, message=FALSE, warning=FALSE}
# Aggregates of energy poverty by access level
population_by_access <- read.csv("/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/population_by_access.csv")
population_by_access$Labels2 <- factor(population_by_access$Labels, levels = c("High Access", "Medium Access", "Low Access", "No/Very Low Access"))
population_by_access <- population_by_access[order(population_by_access$Labels2),]
fig <- plot_ly(population_by_access,
x = ~Labels2,
y = ~Percentage,
type = 'bar',
text = ~label_text,
textposition = 'auto',
marker = list(color = rev(c("#440154FF", "#31688EFF", "#35B779FF", "#FDE725FF")))
)
fig_pop_access <- fig %>% layout(
title = "Est. Population by Access Level",
xaxis = list(title = "", showgrid = FALSE),
yaxis = list(title = "", showgrid = FALSE, showticklabels = FALSE),
uniformtext = list(minsize = 10, mode = 'show')
)
# ---------------------------------------
# State level percentage calculations
# ---------------------------------------
# Data prep
pop_raw <- "/Users/chinpihoikipgen/Downloads/temp_files/GHS_POP_2025.tif"
raster_analyse <- "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/CO REQUESTS/2024/Energy/Access_Solar_Analysis/graphics_presentation/gmm_access.tif"
population <- rast(pop_raw)
raster_analyse <- rast(raster_analyse)
# Nigeria boundaries
nigeria_shape <- st_read(
"/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/GRAPHICS/SHAPEFILES/NIGERIA_SHAPEFILE/nga_admbnda_adm0_osgof_20190417.shp",
quiet = TRUE)
nigeria_shape <- st_make_valid(nigeria_shape) # Need to fix geometry issues
nigeria_vect <- (vect(nigeria_shape)) # For terra package
# Reproject and Resample
if (crs(population) != crs(raster_analyse)) {
population <- project(population, crs(raster_analyse))
}
if (!all(res(population) == res(raster_analyse))) {
access_highres <- resample(raster_analyse, population, method="near")
}
# Mask
population <- mask(population, nigeria_vect)
access_highres <- mask(access_highres, nigeria_vect)
# Create population rasters for each access level
pop_level1 <- population * (access_highres == 1) # No/Very Low Access
pop_level2 <- population * (access_highres == 2) # Low Access
pop_level3 <- population * (access_highres == 3) # Medium Access
pop_level4 <- population * (access_highres == 4) # High Access
# MERGE WITH STATE SHAPEFILE
state_shapefile <- "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/GRAPHICS/SHAPEFILES/NIGERIA_SHAPEFILE/nga_admbnda_adm1_osgof_20190417.shp"
state_boundaries <- vect(state_shapefile)
# Match CRS
if (crs(state_boundaries) != crs(population)) {
state_boundaries <- project(state_boundaries, crs(population))
}
# Check
#same.crs(state_boundaries, population)
# Extract zonal statistics
state_raster <- rasterize(state_boundaries, population, field="ADM1_EN")
state_total <- zonal(population, state_raster, fun="sum", na.rm=TRUE)
state_no_access <- zonal(pop_level1, state_raster, fun="sum", na.rm=TRUE)
state_low_access <- zonal(pop_level2, state_raster, fun="sum", na.rm=TRUE)
state_med_access <- zonal(pop_level3, state_raster, fun="sum", na.rm=TRUE)
state_high_access <- zonal(pop_level4, state_raster, fun="sum", na.rm=TRUE)
# Join Results
state_results <- merge(state_total, state_no_access, by="ADM1_EN")
names(state_results) <- c("ADM1_EN", "total_pop", "no_access_pop")
state_results <- merge(state_results, state_low_access, by="ADM1_EN")
names(state_results)[4] <- "low_access_pop"
state_results <- merge(state_results, state_med_access, by="ADM1_EN")
names(state_results)[5] <- "med_access_pop"
state_results <- merge(state_results, state_high_access, by="ADM1_EN")
names(state_results)[6] <- "high_access_pop"
# Replace NA with zero
state_results$no_access_pop[is.na(state_results$no_access_pop)] <- 0
state_results$low_access_pop[is.na(state_results$low_access_pop)] <- 0
state_results$med_access_pop[is.na(state_results$med_access_pop)] <- 0
state_results$high_access_pop[is.na(state_results$high_access_pop)] <- 0
# Calculate percentages and energy poverty
state_results$no_access_pct <- (state_results$no_access_pop / state_results$total_pop) * 100
state_results$low_access_pct <- (state_results$low_access_pop / state_results$total_pop) * 100
state_results$med_access_pct <- (state_results$med_access_pop / state_results$total_pop) * 100
state_results$high_access_pct <- (state_results$high_access_pop / state_results$total_pop) * 100
state_results$energy_poor_pop <- state_results$no_access_pop + state_results$low_access_pop
state_results$energy_poor_pct <- (state_results$energy_poor_pop / state_results$total_pop) * 100
# Handle any NaN values from division by zero
state_results$no_access_pct[is.nan(state_results$no_access_pct)] <- 0
state_results$low_access_pct[is.nan(state_results$low_access_pct)] <- 0
state_results$med_access_pct[is.nan(state_results$med_access_pct)] <- 0
state_results$high_access_pct[is.nan(state_results$high_access_pct)] <- 0
state_results$energy_poor_pct[is.nan(state_results$energy_poor_pct)] <- 0
# Sort by energy poverty percentage
state_results <- state_results[order(-state_results$energy_poor_pct), ]
#state_results$ADM1_EN[state_results$ADM1_EN == "Federal Capital Territory"] <- "FCT"
# ---------------------------------------
# State level graphics
# ---------------------------------------
# Pivot to long format
state_results_long <- state_results %>%
select(ADM1_EN, no_access_pct, low_access_pct, med_access_pct, high_access_pct) %>%
pivot_longer(
cols = ends_with("_pct"),
names_to = "AccessLevel",
values_to = "Percentage"
) %>%
mutate(
# Clean Access Level names for plotting
AccessLevel = case_when(
AccessLevel == "no_access_pct" ~ "No/Very Low Access",
AccessLevel == "low_access_pct" ~ "Low Access",
AccessLevel == "med_access_pct" ~ "Medium Access",
AccessLevel == "high_access_pct" ~ "High Access"
),
ADM1_EN = factor(ADM1_EN, levels = sort(unique(state_results$ADM1_EN))),
AccessLevel = factor(AccessLevel, levels = c("No/Very Low Access", "Low Access", "Medium Access", "High Access"))
)
state_results_long <- state_results_long[order(state_results_long$ADM1_EN, state_results_long$AccessLevel),]
# Plot
fig <- plot_ly(
data = state_results_long,
x = ~Percentage,
y = ~ADM1_EN,
color = ~AccessLevel,
type = 'bar',
orientation = 'h',
text = ~paste0(ADM1_EN, "(", AccessLevel,"): ",round(Percentage), "%"),
textposition = 'auto',
hoverinfo = 'text',
opacity = 0.70, # adjust this value (e.g., 0.5 to 0.8)
colors = c(
"No/Very Low Access" = "#440154FF", # deep purple
"Low Access" = "#31688EFF", # blue
"Medium Access" = "#35B779FF", # green
"High Access" = "#FDE725FF" # yellow
)
)
# Customize layout
fig_access_by_state <- fig %>% layout(
barmode = 'stack',
title = "Energy Access Levels by State (% population)",
xaxis = list(title = "", ticksuffix = "%", showgrid = FALSE),
yaxis = list(title = "", categoryorder = "array", showgrid = FALSE),
legend = list(orientation = "h", x = -0.01, y = -0.05),
margin = list(l = 100) # adjust left margin to fit state names
)
#knitr::include_graphics("~/Downloads/State_Access_Levels.png")
# Map
state_vect <- vect(state_shapefile)
state_sf <- st_as_sf(state_vect)
state_results$ADM1_EN <- as.character(state_results$ADM1_EN)
state_sf$ADM1_EN <- as.character(state_sf$ADM1_EN)
state_map_data <- left_join(state_sf, state_results, by = "ADM1_EN")
#display.brewer.all(type = "seq")
#energy_pct_colors <- colorRampPalette(c("#f7fcf0", "#e0f3db", "#ccebc5", "#a8ddb5","#7bccc4", "#4eb3d3", "#2b8cbe", "#08589e"))(8)
#energy_pct_colors <- colorRampPalette(c("#F2CB05", "#F2DA5E", "#F2E291", "#F2E8B3", "#F2EBC9"))(8)
energy_pct_colors <- colorRampPalette(c(
"#FFF7BC", # light yellow
"#FEC44F", # golden
"#FE9929", # orange
"#EC7014", # dark orange
"#CC4C02", # burnt orange
"#993404" # deep brown-orange
))(8)
#energy_pct_colors <- colorRampPalette(c("#5C4A72","#4B729C", "#61A57D", "#F5E676"))(8)
pal <- colorNumeric(
palette = (energy_pct_colors),
domain = state_map_data$energy_poor_pct,
na.color = "transparent"
)
leaflet_map <- leaflet(state_map_data) %>%
addProviderTiles(providers$CartoDB.DarkMatter) %>%
addPolygons(
fillColor = ~pal(energy_poor_pct),
weight = 1,
opacity = 1,
color = "white",
dashArray = "3",
fillOpacity = 0.7,
highlight = highlightOptions(
weight = 2,
color = "#666",
fillOpacity = 0.9,
bringToFront = TRUE
),
label = ~paste0(ADM1_EN, ": ", round(energy_poor_pct, 1), "%"),
labelOptions = labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "13px",
direction = "auto"
)
) %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 5.5) %>% # Adjust zoom as needed
#fitBounds(bb["xmin"], bb["ymin"], bb["xmax"], bb["ymax"]) %>%
addLegend(
pal = pal,
values = ~energy_poor_pct,
opacity = 0.7,
title = "Est. Energy Poverty (%)",
position = "bottomright"
) %>%
addControl(
html = HTML("<div style='font-size:10px; color:gray; padding:4px;'>*Note: Energy poverty defined as % population with no/very low or low access areas.</div>"),
position = "topright"
)
# ---------------------------------------
# Priority states
# ---------------------------------------
# temp gradient - need to revisit colors
energy_pct_colors <- colorRampPalette(c(
"#FFF7BC",
"#FEC44F",
"#FE9929",
"#EC7014",
"#CC4C02",
"#993404"
))(100)
fig_priority_states <- plot_ly(
data = state_results,
x = ~energy_poor_pct,
y = ~energy_poor_pop,
type = 'scatter',
mode = 'markers+text',
textposition = 'top center',
hoverinfo = 'text',
hovertext = ~paste0(
"<b>", ADM1_EN, "</b><br>",
"Energy Poor: ", round(energy_poor_pct, 1), "%<br>",
"Pop. Energy Poor: ", format(round(energy_poor_pop), big.mark = ","), "<br>",
"Total Pop: ", format(round(total_pop), big.mark = ",")
),
marker = list(
size = ~sqrt(total_pop) / 200,
sizemode = 'diameter',
sizeref = 2.0 * max(sqrt(state_results$total_pop)) / (100^2),
color = ~energy_poor_pct,
colorscale = list(
list(0, "#FFF7BC"),
list(0.2, "#FEC44F"),
list(0.4, "#FE9929"),
list(0.6, "#EC7014"),
list(0.8, "#CC4C02"),
list(1, "#993404")
),
showscale = TRUE,
colorbar = list(title = "% Energy Poor")
),
text = ~ADM1_EN # Labels for chart
)
fig_priority_states <- fig_priority_states %>% layout(
title = "",
xaxis = list(
title = "% Population in Energy Poverty",
ticksuffix = "%",
gridcolor = "lightgray"
),
yaxis = list(
title = "Total Population in Energy Poverty",
tickformat = ",",
gridcolor = "lightgray"
),
hovermode = "closest",
showlegend = FALSE
)
# HTML tags arragement
browsable(
tagList(
div(
style = "height: 100%; width: 100%;",
div(
style = "display: flex; flex-direction: row; gap: 20px; height: 100%; width: 100%;",
# Left
div(
style = "width: 50%; display: flex; flex-direction: column; height: 100%;",
div(style = "flex: 0.4; overflow: auto; margin-bottom: 10px;", fig_pop_access),
div(style = "flex: 1.6; overflow: auto;", fig_access_by_state)
),
# Right
div(
style = "width: 50%; display: flex; flex-direction: column; gap: 20px;",
div(style = "flex: 1;", leaflet_map),
div(style = "flex: 1;", fig_priority_states)
)
)
)
)
)
```
***
This section disaggregates electricity access and energy poverty at the state level, combining GMM-classified nightlight-based access typologies with high-resolution population estimates (GHS-POP 2025) to reveal the scale and spatial distribution of under-served populations across Nigeria. While these are population estimates derived from the GHS-POP model, they still offer valuable spatial insight into settlement patterns and the population distribution of energy poverty.
By overlaying each electricity access class, ranging from No/Very Low to High Access, with gridded population surfaces (~1 km resolution), the analysis generates population-adjusted estimates of energy deprivation. This allows not only quantification of affected populations, but also identification of where energy poverty is most concentrated within each state.
To visualize these dynamics, the card presents three linked views:
- A national bar chart (top left) estimating population by access level, showing overall distribution;
- A state-level stacked bar chart (bottom left) showing the percentage of each state’s population in each access tier;
- An interactive map (right) depicting the share of population in energy poverty—defined as those residing in No/Very Low or Low Access zones.
- A bubble chart (bottom right) plotting each state by percentage of population in living in No/Very Low and Low energy access areas (X axis) against estimated total energy poor population (Y axis) and the size of the bubble reflecting total population at the state level.
Together, these views highlight both relative and absolute disparities. They help identify priority states with both high energy poverty rates and large affected populations For instance, two states may have similar percentages of energy-poor populations, but vastly different population sizes—amplifying the need for population-adjusted targeting in energy access planning.
### **LGA-Level Energy Poverty Estimates** <br> Hotspots and Localised Deficit Scores
```{r energy-poverty-LGA-level, echo=FALSE, warnings = FALSE}
# ---------------------------------------
# LGA level percentages
# ---------------------------------------
# Load LGA shapefile
lga_shapefile <- "/Users/chinpihoikipgen/Documents/Documents – chinpihoi’s MacBook Pro - 1/NIGERIA/GRAPHICS/SHAPEFILES/NIGERIA_SHAPEFILE/nga_admbnda_adm2_osgof_20190417.shp"
lga_boundaries <- vect(lga_shapefile)
# Match CRS
if (crs(lga_boundaries) != crs(population)) {
lga_boundaries <- project(lga_boundaries, crs(population))
}
# Check
#same.crs(lga_boundaries, population)
# Extract zonal statistics
lga_raster <- rasterize(lga_boundaries, population, field="ADM2_PCODE")
zonal_total <- zonal(population, lga_raster, fun="sum", na.rm=TRUE)
zonal_no_access <- zonal(pop_level1, lga_raster, fun="sum", na.rm=TRUE)
zonal_low_access <- zonal(pop_level2, lga_raster, fun="sum", na.rm=TRUE)
zonal_med_access <- zonal(pop_level3, lga_raster, fun="sum", na.rm=TRUE)
zonal_high_access <- zonal(pop_level4, lga_raster, fun="sum", na.rm=TRUE)
# Create a dataframe from zonal values to LGA names for lookup later
lga_lookup <- data.frame(
ADM2_PCODE = lga_boundaries$ADM2_PCODE,
LGA_Name = lga_boundaries$ADM2_EN,
State = lga_boundaries$ADM1_EN
)
# Join
zonal_results <- merge(zonal_total, zonal_no_access, by="ADM2_PCODE")
names(zonal_results) <- c("ADM2_PCODE", "total_pop", "no_access_pop")
zonal_results <- merge(zonal_results, zonal_low_access, by="ADM2_PCODE")
names(zonal_results)[4] <- "low_access_pop"
zonal_results <- merge(zonal_results, zonal_med_access, by="ADM2_PCODE")
names(zonal_results)[5] <- "med_access_pop"
zonal_results <- merge(zonal_results, zonal_high_access, by="ADM2_PCODE")
names(zonal_results)[6] <- "high_access_pop"
# Replace NA with zero
zonal_results$no_access_pop[is.na(zonal_results$no_access_pop)] <- 0
zonal_results$low_access_pop[is.na(zonal_results$low_access_pop)] <- 0
zonal_results$med_access_pop[is.na(zonal_results$med_access_pop)] <- 0
zonal_results$high_access_pop[is.na(zonal_results$high_access_pop)] <- 0
# Add LGA and state maps
zonal_results <- merge(zonal_results, lga_lookup, by="ADM2_PCODE")
# Calculate percentages and energy poverty
zonal_results$no_access_pct <- (zonal_results$no_access_pop / zonal_results$total_pop) * 100
zonal_results$low_access_pct <- (zonal_results$low_access_pop / zonal_results$total_pop) * 100
zonal_results$med_access_pct <- (zonal_results$med_access_pop / zonal_results$total_pop) * 100
zonal_results$high_access_pct <- (zonal_results$high_access_pop / zonal_results$total_pop) * 100
zonal_results$energy_poor_pop <- zonal_results$no_access_pop + zonal_results$low_access_pop
zonal_results$energy_poor_pct <- (zonal_results$energy_poor_pop / zonal_results$total_pop) * 100
# NaN values handling
zonal_results$no_access_pct[is.nan(zonal_results$no_access_pct)] <- 0
zonal_results$low_access_pct[is.nan(zonal_results$low_access_pct)] <- 0
zonal_results$med_access_pct[is.nan(zonal_results$med_access_pct)] <- 0
zonal_results$high_access_pct[is.nan(zonal_results$high_access_pct)] <- 0
zonal_results$energy_poor_pct[is.nan(zonal_results$energy_poor_pct)] <- 0
# Sort by energy poverty percentage
zonal_results <- zonal_results[order(-zonal_results$energy_poor_pct), ]
# Deficit score - normalised
zonal_results <- zonal_results %>%
mutate(
norm_pct = scales::rescale(energy_poor_pct), # 0–1 scale
norm_pop = scales::rescale(energy_poor_pop), # 0–1 scale
composite_score = round(0.5 * norm_pct + 0.5 * norm_pop,2) # or weighted
)
# Merge with lga boundaries data
lga_boundaries_merged <- merge(lga_boundaries, zonal_results, by="ADM2_PCODE", all.x=TRUE)
# Replace NA with zero for energy-related fields
lga_boundaries_merged$no_access_pop[is.na(lga_boundaries_merged$no_access_pop)] <- 0
lga_boundaries_merged$low_access_pop[is.na(lga_boundaries_merged$low_access_pop)] <- 0
lga_boundaries_merged$med_access_pop[is.na(lga_boundaries_merged$med_access_pop)] <- 0
lga_boundaries_merged$high_access_pop[is.na(lga_boundaries_merged$high_access_pop)] <- 0
lga_boundaries_merged$energy_poor_pop[is.na(lga_boundaries_merged$energy_poor_pop)] <- 0
lga_boundaries_merged$energy_poor_pct[is.na(lga_boundaries_merged$energy_poor_pct)] <- 0
# ---------------------------------------
# LGA level maps
# ---------------------------------------
pop_breaks <- pretty(lga_boundaries_merged$composite_score, n = 8)
# Create color function (colorBin for breaks, or colorQuantile for quantiles)
pop_pal <- colorBin(
palette = c("#f1eef6", "#d7b5d8", "#df65b0", "#dd1c77", "#980043"),
domain = lga_boundaries_merged$composite_score,
bins = pop_breaks,
na.color = "transparent"
)
leaflet_lga <- leaflet(lga_boundaries_merged) %>%
addProviderTiles(providers$CartoDB.DarkMatter) %>%
addPolygons(
fillColor = ~pop_pal(composite_score),
weight = 0.5,
color = "#ffffff",
fillOpacity = 0.8,
label = ~paste0(LGA_Name, " (", State, "): ", round(energy_poor_pct, 1), "% energy poor; ", "Energy Access Deficit Score: ",composite_score ),
labelOptions = labelOptions(
style = list("font-weight" = "normal", padding = "3px 8px"),
textsize = "12px",
direction = "auto"
),
highlightOptions = highlightOptions(
weight = 2,
color = "#666",
fillOpacity = 0.9,
bringToFront = TRUE
)
) %>%
setView(lng = 8.6753, lat = 9.0820, zoom = 6) %>% # zoom
addLegend(
pal = pop_pal,
values = ~composite_score,
title = "Energy Access Deficit Score",
position = "bottomright",
opacity = 0.7
) %>%
addControl(
html = HTML("<div style='font-size:10px; color:gray; padding:4px;'>*Note: Energy poverty defined as % population with no/very low or low access areas.</div>"),
position = "topright"
)
# ---------------------------------------
# Top 50
# ---------------------------------------
# Filter top 50 LGAs based on composite score
top_lgas <- zonal_results %>%
arrange(desc(composite_score)) %>%
slice(1:50) %>%
mutate(label = paste0(LGA_Name, " (", State, ")")) # For y-axis labels
# Plot
fig_top50 <- plot_ly(
data = top_lgas,
x = ~composite_score,
y = ~reorder(label, composite_score),
type = 'bar',
orientation = 'h',
marker = list(color = "#980043"),
hoverinfo = 'text',
text = ~paste0(
"<b>", LGA_Name, " (", State, ")</b><br>",
"Energy Poor (%): ", round(energy_poor_pct, 1), "%<br>",
"Population Energy Poor: ", format(round(energy_poor_pop), big.mark = ","), "<br>",
"Score: ", composite_score
)
) %>% layout(
title = "Top 50 LGAs by Energy Access Deficit Score",
xaxis = list(title = "Deficit Score"),
yaxis = list(title = "", automargin = TRUE),
margin = list(l = 150)
)
# ---------------------------------------
# LGA level table
# ---------------------------------------
zonal_results_table <- zonal_results %>%
select(LGA_Name, State, no_access_pct, low_access_pct, med_access_pct, high_access_pct, energy_poor_pct, composite_score) %>%
mutate(across(ends_with("pct"), ~round(.x, 2))) %>%
rename(
`No Access (%)` = no_access_pct,
`Low Access (%)` = low_access_pct,
`Medium Access (%)` = med_access_pct,
`High Access (%)` = high_access_pct,
`Energy Poor (%)` = energy_poor_pct,
`Composite Score` = composite_score
)
table_lga <- DT::datatable(
zonal_results_table,
options = list(
pageLength = 80,
scrollX = TRUE,
autoWidth = TRUE
),
rownames = FALSE,
filter = 'top',
caption = htmltools::tags$caption("LGA-Level Energy Access Summary")
) %>%
formatStyle(
'Composite Score',
background = styleColorBar(zonal_results_table$`Composite Score`, '#dd1c77'),
backgroundSize = '80% 70%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center'
)
browsable(
tagList(
div(
style = "display: flex; flex-direction: row; height: 100%; gap: 20px;",
# Left column
div(
style = "flex: 1; height: 100%; overflow: auto;",
table_lga # or stacked charts div
),
# Right
div(
style = "flex: 1; height: 100%;",
leaflet_lga
)
)
)
)
```
***
The analysis was extended to cater for a more operational and locally actionable scale, energy access estimates were disaggregated to Nigeria’s 774 LGAs. This LGA-level view allows for finer geographic targeting and supports more tailored interventions, especially in high-burden and under-served communities that may be overlooked in state-level averages.
To synthesize two dimensions of energy poverty intensity (% of population without access) and scale (absolute number affected), a composite **Energy Access Deficit** score was developed. This score combines normalized values of energy poverty percentage and estimated energy-poor population to flag LGAs where unmet energy needs are both widespread and numerically large.
The interactive map displays LGA-level Energy Access Deficit scores across Nigeria, while the accompanying searchable table provides access statistics and scores by LGA and state.
**Note:** Population estimates are derived from modeled data (GHS-POP 2025) and should be interpreted as indicative. While not based on recent census data, they offer valuable insight into spatial patterns of deprivation and remain useful for planning and prioritization.
```{r, eval = FALSE}
browsable(
tagList(
div(
style = "display: flex; flex-direction: row; gap: 20px; height: 100%;",
# Left column: LGA table
div(
style = "width: 50%; height: 100%; overflow: auto;",
table_lga
),
# Right column: Map + Bar chart stacked
div(
style = "width: 50%; display: flex; flex-direction: column; gap: 20px;",
# Map (top)
div(
style = "flex: 1; height: 50%;",
leaflet_lga
),
# Top 50 LGAs chart (bottom)
div(
style = "flex: 1; height: 50%; overflow: auto;",
fig_top50
)
)
)
)
)
```
### **Energy Access in Health Facilities** Mapping Energy Deprivation Across Public Hospitals
```{r, energy-poverty-health, echo = FALSE, warnings = FALSE, eval=FALSE }
hospital_data <- read.csv("/Users/chinpihoikipgen/Downloads/temp_files/GRID3_NGA_health_facilities_V2.csv")
hospital_data <- hospital_data %>%
filter(ownership_type%in%c("Local Government","State Government", "Federal Government"))
# Convert hospitals to a spatial object
hospitals_sf <- vect(hospital_data, geom = c("longitude", "latitude"), crs = "EPSG:4326")
# Reproject to match access raster's CRS
hospitals_sf <- project(hospitals_sf, crs(raster_data))
# Check
#same.crs(hospitals_sf, raster_data)
# Extract access level for each hospital
extracted_values <- terra::extract(access_highres, hospitals_sf)
# Check structure
#str(extracted_values)
#head(extracted_values)
# Assign values to hospital
hospitals_sf$access_level <- extracted_values[,2]
hospitals_sf$access_category <- factor(hospitals_sf$raster_data,
levels = 1:4,
labels = c("No/Very Low Access",
"Low Access",
"Medium Access",
"High Access"))
# Check distribution
#table(hospitals_sf$access_category)
# Hospitals in each access level
hospitals_level1 <- hospitals_sf[hospitals_sf$raster_data == 1,] # No/Very Low Access
hospitals_level2 <- hospitals_sf[hospitals_sf$raster_data == 2,] # Low Access
hospitals_level3 <- hospitals_sf[hospitals_sf$raster_data == 3,] # Medium Access
hospitals_level4 <- hospitals_sf[hospitals_sf$raster_data == 4,] # High Access
# Plot each access level with hospital overlay
par(mfrow=c(2,2))
# No/Very Low Access
plot(access_level1_mask, main="No/Very Low Access Areas",
col=c("white", cluster_colors[1]), legend=FALSE)
plot(hospitals_level1, col="red", pch=16, cex=0.2, add=TRUE)
plot(nigeria_vect, add=TRUE, border="black", lwd=0.5)
legend("bottomright", legend=paste0("Hospitals: ", nrow(hospitals_level1)),
cex=0.7, bty="n")
# Low Access
plot(access_level2_mask, main="Low Access Areas",
col=c("white", cluster_colors[2]), legend=FALSE)
plot(hospitals_level2, col="red", pch=16, cex=0.2, add=TRUE)
plot(nigeria_vect, add=TRUE, border="black", lwd=0.5)
legend("bottomright", legend=paste0("Hospitals: ", nrow(hospitals_level2)),
cex=0.7, bty="n")
# Medium Access
plot(access_level3_mask, main="Medium Access Areas",
col=c("white", cluster_colors[3]), legend=FALSE)
plot(hospitals_level3, col="red", pch=16, cex=0.2, add=TRUE)
plot(nigeria_vect, add=TRUE, border="black", lwd=0.5)
legend("bottomright", legend=paste0("Hospitals: ", nrow(hospitals_level3)),
cex=0.7, bty="n")
# High Access
plot(access_level4_mask, main="High Access Areas",
col=c("white", cluster_colors[4]), legend=FALSE)
plot(hospitals_level4, col="red", pch=16, cex=0.2, add=TRUE)
plot(nigeria_vect, add=TRUE, border="black", lwd=0.5)
legend("bottomright", legend=paste0("Hospitals: ", nrow(hospitals_level4)),
cex=0.7, bty="n")
```
***
[graphics to be rendered]
This section overlays public hospital locations with GMM-classified electricity access typologies to highlight the availability of energy infrastructure at health facilities across Nigeria. By categorizing each hospital into four access levels—No/Very Low, Low, Medium, and High—the analysis identifies critical service points operating under severe energy constraints.
Public hospitals in No/Very Low Access zones often serve as primary health centers for underserved communities. Identifying and mapping these facilities provides a clear entry point for infrastructure investment planning—especially solarization or mini-grid solutions.
To support prioritization, the dashboard presents:
- A state-level bar chart showing the percentage of hospitals in No/Low Access areas
- An interactive map of hospital points, color-coded by access level
- Summary counts of hospitals per access level
**Note:** Only public hospitals were included (federal, state, and local government facilities).
**Critical Healthcare Infrastructure Findings:**
- **68% of public healthcare facilities** operate in areas with poor electricity access
- **892 hospitals and health centers** have very low/no electricity access
- **1,526 facilities** (68% of total) are classified as "energy poor"
**Priority States for Healthcare Energy Interventions:**
The northern states of Borno, Katsina, Bauchi, and Jigawa emerge as top priorities, combining high numbers of energy-poor healthcare facilities with large populations lacking electricity access.
This analysis underscores the critical intersection between energy poverty and healthcare delivery, where reliable electricity is essential for medical equipment, vaccine cold chains, lighting for night deliveries, and overall healthcare quality.
### **Energy Poverty and Solar Potential** <br> Strategic Opportunities for Renewable Energy
```{r, energy-solar, echo = FALSE}
```
***
To do